package com.bing.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import javax.xml.bind.*;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Created by kaibb on 2014/11/29.
 */
public class JaxbXmlHandler {

    private final static Logger logger = LoggerFactory.getLogger(JaxbXmlHandler.class);
    private final static ConcurrentMap<Class<?>, JAXBContext> jaxbContexts = new ConcurrentHashMap<Class<?>, JAXBContext>(64);
    public static final String ESCAPE_HANDLER = "com.sun.xml.bind.characterEscapeHandler";

    public static String convertToXml(Object obj) {
        String result = "";
        try {
            logger.info("marshaller :{}",obj.getClass());
            JAXBContext context = JAXBContext.newInstance(obj.getClass());
            Marshaller marshaller = context.createMarshaller();
            logger.debug("marshaller Name:{}", marshaller.getClass().getName());
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
//去掉生成xml的默认报文头。
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
//转换所有的适配字符，包括xml实体字符&lt;和&gt;，xml实体字符在好多处理xml
//的框架中是处理不了的，除非序列化。
            marshaller.setProperty(ESCAPE_HANDLER,
                    new MyCharacterEscapeHandler());
            StringWriter writer = new StringWriter();
//添加自己想要的xml报文头
            marshaller.marshal(obj, writer);
            result = writer.toString();
        } catch (UnmarshalException ex) {
            logger.error("UnMarshaller error:{} detail:", obj, ex);
        } catch (JAXBException e) {
            logger.error("JAXBException error:{}", obj, e);
        }
        return result;
    }

    public static <T> T convertToObj(String xml, Class<? extends T> clazz) {
        T value = null;
        try {
            JAXBContext context = getJaxbContext(clazz);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            StringReader stringReader = new StringReader(xml);
            value = (T) unmarshaller.unmarshal(stringReader);

        } catch (UnmarshalException ex) {
            logger.error("UnMarshaller error:{} detail:", xml, ex);
        } catch (JAXBException e) {
            logger.error("JAXBException error:{} detail:", xml, e);
        }
        return value;
    }

    private static JAXBContext getJaxbContext(Class<?> clazz) {
        Assert.notNull(clazz, "'clazz' must not be null");
        JAXBContext jaxbContext = jaxbContexts.get(clazz);
        if (jaxbContext == null) {
            try {
                jaxbContext = JAXBContext.newInstance(clazz);
                jaxbContexts.putIfAbsent(clazz, jaxbContext);
            } catch (JAXBException ex) {
                throw new HttpMessageConversionException(
                        "Could not instantiate JAXBContext for class [" + clazz + "]: " + ex.getMessage(), ex);
            }
        }
        return jaxbContext;
    }
}
